﻿#include "framework.h"
#include <stdio.h>
#include "Const.h"
#include "Game.h"

// 全局变量
BaseGameObject* gBaseGameObjects[GAME_BOARD_GRID_COUNT_V][GAME_BOARD_GRID_COUNT_H];   // 所有游戏物体

// 函数声明
static void Game_Delete(Game*);
static void Game_Clear(Game*);
static void Game_EnterLevel(Game*);
static void Game_GameOver(Game*, BOOL);
static void Game_GotoLevel(Game*, int);
static void Game_GotoNextLevel(Game*);
static BOOL Game_IsWon(Game*);
static BOOL Game_OnKeyPress(Game*, UINT);
static void Game_Restart(Game*);
static void Game_Undo(Game*);

/**
 * 创建游戏。
 * @param callback  回调
 * @param drawer    绘图者
 * @param gameBoard 游戏区域
 * @return 游戏
 */
Game* New_Game(HWND callback, HDC drawer, HWND gameBoard) {
    Game* game = (Game*)calloc(1, sizeof(Game));

    // 初始化数据
    game->mCallback = callback;
    game->mDrawer = drawer;
    game->mGameBoard = gameBoard;
    game->mLevel = 1;
    game->mStatus = END;

    // 初始化函数
    game->deleteSelf = Game_Delete;
    game->restart = Game_Restart;
    game->gotoLevel = Game_GotoLevel;
    game->undo = Game_Undo;
    game->onKeyPressed = Game_OnKeyPress;

    // 进入第1关
    Game_EnterLevel(game);

    return game;
}

/**
 * 删除游戏。
 * @param game  游戏
 */
static void Game_Delete(Game* game) {
    Game_Clear(game);
    free(game);
}

/**
 * 清除所有对象。
 * @param game  游戏
 */
static void Game_Clear(Game* game) {
    int i = 0, j = 0;
    MoveNode* previous = game->mMoveList;

    // 删除所有游戏物体
    for (i = 0; i < GAME_BOARD_GRID_COUNT_V; i++) {
        for (j = 0; j < GAME_BOARD_GRID_COUNT_H; j++) {
            if (gBaseGameObjects[i][j] != NULL) {
                gBaseGameObjects[i][j]->deleteSelf(gBaseGameObjects[i][j]);
            }
        }
    }
    ZeroMemory(gBaseGameObjects, sizeof(gBaseGameObjects));

    // 删除所有移动结点
    while (game->mMoveList != NULL) {
        previous = game->mMoveList->previous;
        free(game->mMoveList);
        game->mMoveList = previous;
    }
}

/**
 * 进入关卡。
 * @param game  游戏
 */
static void Game_EnterLevel(Game* game) {
    TCHAR levelFilePath[MAX_PATH] = { 0 };
    FILE* file = NULL;
    RECT gameBoardRect = { 0, 0, GAME_BOARD_WIDTH, GAME_BOARD_HEIGHT };
    char buffer = 0;
    GameObjectType type = NOTHING;
    int x = 0, y = 0;

    // 重绘游戏区域
    SetBkColor(game->mDrawer, GAME_BOARD_COLOR);
    ExtTextOut(game->mDrawer, 0, 0, ETO_OPAQUE, &gameBoardRect, NULL, 0, NULL);

    // 删除所有物体
    Game_Clear(game);

    // 读取关卡文件
    _stprintf_s(levelFilePath, MAX_PATH, LEVEL_FILE_PATH, game->mLevel);
    _tfopen_s(&file, levelFilePath, _T("r"));

    // 创建所有物体
    game->mBoxCount = 0;
    for (y = 0; y < GAME_BOARD_GRID_COUNT_V; y++) {
        for (x = 0; x < GAME_BOARD_GRID_COUNT_H; x++) {
            fread(&buffer, 1, 1, file);
            if (buffer == '0') {
                continue;
            }
            if (buffer == '\r' || buffer == '\n') {
                x--;
                continue;
            }

            type = (GameObjectType)atoi(&buffer);

            // 创建箱子
            if (type == BOX) {
                gBaseGameObjects[y][x] = New_Box(New_BaseGameObject(game->mDrawer, BACKGROUND, x, y), game->mDrawer, type, x, y);
                game->mBoxCount++;
                continue;
            }

            // 创建小人
            if (type == MAN) {
                gBaseGameObjects[y][x] = New_Man(New_BaseGameObject(game->mDrawer, BACKGROUND, x, y), game->mDrawer, game->mGameBoard, type, x, y);
                game->mMan = (Man*)(((Box*)(gBaseGameObjects[y][x]->mChild))->mChild);
                continue;
            }

            // 创建背景、目的地、墙
            gBaseGameObjects[y][x] = New_BaseGameObject(game->mDrawer, type, x, y);
        }
    }

    // 关闭文件
    fclose(file);

    // 重置成员变量
    game->mMoveCount = 0;
    game->mStatus = PLAYING;

    // 刷新游戏区域
    InvalidateRect(game->mGameBoard, NULL, FALSE);

    // 发送通知
    SendMessage(game->mCallback, UM_GOTO_LEVEL, game->mLevel, 0);
}

/**
 * 游戏结束。
 * @param game      游戏
 * @param allOver   全部通关则为 TRUE，否则为 FALSE
 */
static void Game_GameOver(Game* game, BOOL allOver) {
    // 将状态设置为已结束
    game->mStatus = END;

    // 发送通知
    SendMessage(game->mCallback, UM_GAME_OVER, allOver, 0);
}

/**
 * 进入指定的关卡。
 * @param game  游戏
 * @param level 关卡数
 */
static void Game_GotoLevel(Game* game, int level) {
    if (level <= LEVEL_COUNT) {
        game->mLevel = level;
        Game_EnterLevel(game);
    }
}

/**
 * 进入下一关。
 * @param game  游戏
 */
static void Game_GotoNextLevel(Game* game) {
    if (game->mLevel < LEVEL_COUNT) {
        game->mLevel++;
        Game_EnterLevel(game);
    }
}

/**
 * 判断是否胜利。
 * @param game  游戏
 * @return 胜利则返回 TRUE，否则返回 FALSE
 */
static BOOL Game_IsWon(Game* game) {
    return game->mBoxCount == 0;
}

/**
 * 按键事件的响应函数。
 * @param game  游戏
 * @param key   按键
 * @return 事件在此被处理则返回 TRUE，否则返回 FALSE
 */
static BOOL Game_OnKeyPress(Game* game, UINT key) {
    MoveResult moveResult = NO_MOVE;
    BaseGameObject* movedBox = NULL;
    MoveNode* previous = NULL;
    BOOL allOver = FALSE;

    // 如果没在玩，直接返回
    if (game->mStatus != PLAYING) {
        return FALSE;
    }

    // 如果按的不是方向键，则直接返回
    if (key < VK_LEFT || key > VK_DOWN) {
        return FALSE;
    }

    // 移动小人。如果不能移动，则直接返回
    movedBox = game->mMan->move(game->mMan, key, &moveResult);
    if (moveResult <= NO_MOVE) {
        return TRUE;
    }

    // 重绘游戏区域
    InvalidateRect(game->mGameBoard, NULL, FALSE);

    // 增加移动次数
    game->mMoveCount++;

    // 发送通知
    SendMessage(game->mCallback, UM_MAN_MOVED, game->mMoveCount, 0);

    // 判断是否胜利
    if (moveResult == ARRIVED_ON_DEST) {
        game->mBoxCount--;
        if (Game_IsWon(game)) {
            allOver = game->mLevel == LEVEL_COUNT;
            Game_GameOver(game, allOver);
            if (!allOver) {
                Game_GotoNextLevel(game);
            }
            return TRUE;
        }
    } else if (moveResult == LEFT_FROM_DEST) {
        game->mBoxCount++;
    }

    // 将本次移动加入列表
    previous = game->mMoveList;
    game->mMoveList = (MoveNode*)calloc(1, sizeof(MoveNode));
    game->mMoveList->box = movedBox;
    game->mMoveList->direction = key;
    game->mMoveList->previous = previous;

    return TRUE;
}

/**
 * 重玩本关。
 * @param game  游戏
 */
static void Game_Restart(Game* game) {
    game->gotoLevel(game, game->mLevel);
}

/**
 * 回退，即撤消上一次操作。
 * @param game  游戏
 */
static void Game_Undo(Game* game) {
    MoveNode* moveListNode = game->mMoveList;
    MoveResult moveResult = NO_MOVE;

    if (game->mMoveCount == 0) {
        return;
    }
    
    // 计算方向（方向与最后一次移动的方向相反）
    UINT direction = 0;
    switch (moveListNode->direction) {
        case VK_DOWN:
            direction = VK_UP;
            break;
        case VK_LEFT:
            direction = VK_RIGHT;
            break;
        case VK_RIGHT:
            direction = VK_LEFT;
            break;
        case VK_UP:
            direction = VK_DOWN;
            break;
    }

    // 移动小人
    game->mMan->move(game->mMan, direction, &moveResult);

    // 移动箱子
    if (moveListNode->box != NULL) {
        moveListNode->box->move(moveListNode->box, direction, &moveResult);
        if (moveResult == LEFT_FROM_DEST) {
            game->mBoxCount++;
        } else if (moveResult == ARRIVED_ON_DEST) {
            game->mBoxCount--;
        }
    }

    // 删除当前的移动结点
    game->mMoveList = moveListNode->previous;
    free(moveListNode);

    // 发送通知
    game->mMoveCount--;
    SendMessage(game->mCallback, UM_MAN_MOVED, game->mMoveCount, 0);
}
